package com.ib.algokit;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.activequant.broker.IBroker;
import org.activequant.container.services.ITimeStampService;
import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.Symbol;
import org.activequant.core.domainmodel.marketscanner.MarketScan;
import org.activequant.core.domainmodel.marketscanner.MarketScanChange;
import org.activequant.core.domainmodel.marketscanner.ScanCriteria;
import org.activequant.core.types.Currency;
import org.activequant.core.types.SecurityType;
import org.activequant.dao.IBrokerAccountDao;
import org.activequant.dao.ICandleDao;
import org.activequant.dao.IMarketDepthEventDao;
import org.activequant.dao.IMarketScanDao;
import org.activequant.dao.IQuoteDao;
import org.activequant.dao.IScanCriteriaDao;
import org.activequant.dao.ISpecificationDao;
import org.activequant.dao.ISpecificationDetailsDao;
import org.activequant.dao.ITickerNewsDao;
import org.activequant.dao.ITradeIndicationDao;
import org.activequant.data.retrieval.ICandleSeriesSource;
import org.activequant.data.retrieval.ICandleSubscriptionSource;
import org.activequant.data.retrieval.IMarketDepthEventSubscriptionSource;
import org.activequant.data.retrieval.IMarketScanChangeSubscriptionSource;
import org.activequant.data.retrieval.IMarketScanSubscription;
import org.activequant.data.retrieval.IMarketScanSubscriptionSource;
import org.activequant.data.retrieval.IQuoteSeriesSource;
import org.activequant.data.retrieval.IQuoteSubscriptionSource;
import org.activequant.data.retrieval.ITickerNewsSeriesSource;
import org.activequant.data.retrieval.ITickerNewsSubscriptionSource;
import org.activequant.data.retrieval.ITradeIndicationSeriesSource;
import org.activequant.data.retrieval.ITradeIndicationSubscriptionSource;
import org.activequant.data.retrieval.ib.IBContractDetailsSource;
import org.activequant.data.retrieval.mock.MockMarketScanChangeSubscriptionSource;
import org.activequant.tradesystem.annotation.Inject;
import org.activequant.tradesystem.annotation.Start;
import org.activequant.tradesystem.ib.BacktestRunner;
import org.activequant.tradesystem.ib.MarketScanHelper;
import org.activequant.tradesystem.ib.ScanBacktestRunner;
import org.activequant.tradesystem.types.Mode;
import org.activequant.util.ib.IBTwsConnection;
import org.activequant.util.pattern.events.IEventListener;
import org.apache.log4j.Logger;

import com.ib.algokit.forex.CurrencyPairs;
import com.ib.algokit.ib.IBPortfolioManagingBroker;
import com.ib.algokit.marketscan.MarketScanChangeListenerHandlingStrategy;
import com.ib.algokit.marketscan.MarketScanListenerHandlingStrategy;
import com.ib.algokit.marketscan.ThreadPerInstrumentBaseStrategy.InstrumentAnalyzingThread;

/**
 * Holds access to resources and initializes everything
 */
public class TraderProxy {	
	
	private final static Logger log = Logger.getLogger(TraderProxy.class); 
	/**
	 * Inject<br/> 
	 * private ITradeIndicationSubscriptionSource tradeIndicationSubscriptionSource;
	 */
	@Inject 
	private ITradeIndicationSubscriptionSource tradeIndicationSubscriptionSource;
	/**
	 * Inject<br/>
	 * private ICandleSubscriptionSource candleSubscriptionSource;
	 */
	@Inject 
	private ICandleSubscriptionSource candleSubscriptionSource;
	/**
	 * Inject<br/>
	 * private IQuoteSubscriptionSource quoteSubscriptionSource;
	 */
	@Inject 
	private IQuoteSubscriptionSource quoteSubscriptionSource;
	
	/**
	 * DAOs start here<br/>
	 * Inject<br/>
	 * private ISpecificationDao instrumentSpecificationDao;
	 */
	@Inject
	private ISpecificationDao instrumentSpecificationDao;
	/**
	 * Inject<br/>
	 * private IQuoteDao quoteDao;
	 */
	@Inject
	private IQuoteDao quoteDao;
	/**
	 * Inject<br/>
	 * private ITickerNewsDao tickerNewsDao;
	 */
	@Inject
	private ITickerNewsDao tickerNewsDao;
	/**
	 * Inject<br/>
	 * private ITradeIndicationDao tradeIndicationDao;
	 */
	@Inject
	private ITradeIndicationDao tradeIndicationDao;
	/**
	 * Inject<br/>
	 * private IMarketDepthDao marketDepthDao;
	 */
//	@Inject
//	private IMarketDepthDao marketDepthDao;
	/**
	 * Inject<br/>
	 * private IMarketDepthDao marketDepthDao;
	 */
	@Inject
	private IMarketDepthEventDao marketDepthEventDao;
	/**
	 * Inject<br/>
	 * private ICandleDao candleDao;
	 */
	@Inject
	private ICandleDao candleDao;
	/**
	 * Inject<br/>
	 * private IMarketScanDao marketScanDao;
	 */
	@Inject
	private IMarketScanDao marketScanDao;
	/**
	 * Inject<br/>
	 * private IScanCriteriaDao scanCriteriaDao;
	 */
	@Inject
	private IScanCriteriaDao scanCriteriaDao;
	/**
	 * Inject<br/>
	 * private ISpecificationDetailsDao specificationDetailsDao;
	 */
	@Inject
	private ISpecificationDetailsDao specificationDetailsDao;
	/**
	 * Inject<br/>
	 * private IBrokerAccountDao brokerAccountDao;
	 */
	@Inject
	private IBrokerAccountDao brokerAccountDao;
	/**
	 * Inject<br/>
	 * private ICandleSeriesSource candleSeriesSource;
	 */
	@Inject
	private ICandleSeriesSource candleSeriesSource;
	/**
	 * Inject(optional=true)<br/>
	 * private ITradeIndicationSeriesSource tradeIndicationSeriesSource;
	 */
	@Inject(optional=true)
	private ITradeIndicationSeriesSource tradeIndicationSeriesSource;
	/**
	 * Inject(optional=true)<br/>
	 * private IQuoteSeriesSource quoteSeriesSource;
	 */
	@Inject(optional=true)
	private IQuoteSeriesSource quoteSeriesSource; //bid/ask sizes are -1, don't rely on them
	/**
	 * Inject(optional=true)<br/>
	 * private ITickerNewsSeriesSource tickerNewsSeriesSource;
	 */
	@Inject(optional=true)
	private ITickerNewsSeriesSource tickerNewsSeriesSource; 
	/**
	 * Inject<br/>
	 * private IBTwsConnection ibConnection;
	 */
	@Inject
	private IBTwsConnection ibConnection;
	/**
	 * Inject<br/>
	 * private IBroker broker;
	 */
	@Inject
	private IBroker broker;
	/**
	 * Inject<br/>
	 * private IBContractDetailsSource contractDetailsSource;
	 */
	@Inject(optional=true)
	private IBContractDetailsSource contractDetailsSource;
	/**
	 * Inject<br/>
	 * private IMarketScanSubscriptionSource marketScanSubscriptionSource;
	 */
	@Inject(optional=true)
	private IMarketScanSubscriptionSource marketScanSubscriptionSource;
	/**
	 * Inject<br/>
	 * private IMarketScanChangeSubscriptionSource marketScanChangeSubscriptionSource;
	 */
	@Inject(optional=true)
	private IMarketScanChangeSubscriptionSource marketScanChangeSubscriptionSource;
	/**
	 * Inject<br/>
	 * private IMarketDepthEventSubscriptionSource marketDepthEventSubscriptionSource;
	 */
	@Inject(optional=true)
	private IMarketDepthEventSubscriptionSource marketDepthEventSubscriptionSource;
	/**
	 * Inject<br/>
	 * private ITickerNewsSubscriptionSource tickerNewsSubscriptionSource;
	 */
	@Inject(optional=true)
	private ITickerNewsSubscriptionSource tickerNewsSubscriptionSource;
	/**
	 * Inject<br/>
	 * private ITimeStampService timeStampService;
	 */
	@Inject
	private ITimeStampService timeStampService;
	/**
	 * private MarketScanHelper marketScanHelper=null;
	 */
	private MarketScanHelper marketScanHelper=null;
	/**
	 * private IEventListener&lt;MarketScanChange&gt; marketScanChangeListener=null;
	 */
	private IEventListener<MarketScanChange> marketScanChangeListener=null;
	/**
	 * private IEventListener&lt;MarketScan&gt; marketScanListener=null;
	 */
	private IEventListener<MarketScan> marketScanListener=null;
	/**
	 * protected final Map&lt;InstrumentSpecification, InstrumentAnalyzingThread&gt; analyzerThreads = new ConcurrentHashMap&lt;InstrumentSpecification, InstrumentAnalyzingThread&gt;();
	 */
	protected final Map<InstrumentSpecification, InstrumentAnalyzingThread> analyzerThreads = new ConcurrentHashMap<InstrumentSpecification, InstrumentAnalyzingThread>();	
	
	//mocking objects
	MockMarketScanChangeSubscriptionSource mockMarketScanChangeSubscriptionSource=new MockMarketScanChangeSubscriptionSource();	
	/**
	 * returns the associated analyzerThreads(Map&lt;InstrumentSpecification, InstrumentAnalyzingThread&gt;)
	 * @return
	 */
	public Map<InstrumentSpecification, InstrumentAnalyzingThread> getAnalyzerThreads() {
		return analyzerThreads;
	}
	/**
	 * Returns the associated candleSeriesSource(ICandleSeriesSource) (injected)
	 * @return
	 */
	public ICandleSeriesSource getCandleSeriesSource() {
		return candleSeriesSource;
	}
	/**
	 * Returns the associated ibConnection(IBTwsConnection) (injected)
	 * @return
	 */
	public IBTwsConnection getIbConnection() {
		return ibConnection;
	}
	/**
	 * Returns the associated broker(IBroker) (injected)
	 * @return
	 */
	public IBroker getBroker() {
		return broker;
	}
	/**
	 * Returns the associated timeStampService(ITimeStampService) (injected)
	 * @return
	 */
	public ITimeStampService getTimeStampService() {
		return timeStampService;
	}
	/**
	 * Returns the associated quoteSeriesSource(IQuoteSeriesSource) (injected)
	 * @return
	 */
	public IQuoteSeriesSource getQuoteSeriesSource() {
		return quoteSeriesSource;
	}
	/**
	 * Returns the associated tickerNewsSeriesSource(ITickerNewsSeriesSource) (injected)
	 * @return
	 */
	public ITickerNewsSeriesSource getTickerNewsSeriesSource() {
		return tickerNewsSeriesSource;
	}
	/**
	 * Returns the associated candleSubscriptionSource(ICandleSubscriptionSource) (injected)
	 * @return
	 */
	public ICandleSubscriptionSource getCandleSubscriptionSource() {
		return candleSubscriptionSource;
	}
	/**
	 * Returns the associated quoteSubscriptionSource(IQuoteSubscriptionSource) (injected)
	 * @return
	 */
	public IQuoteSubscriptionSource getQuoteSubscriptionSource() {
		return quoteSubscriptionSource;
	}
	/**
	 * Returns the associated tradeIndicationSubscriptionSource(ITradeIndicationSubscriptionSource) (injected)
	 * @return
	 */
	public ITradeIndicationSubscriptionSource getTradeIndicationSubscriptionSource() {
		return tradeIndicationSubscriptionSource;
	}
	/**
	 * Returns the associated marketScanSubscriptionSource(IMarketScanSubscriptionSource) (injected)
	 * @return
	 */
	public IMarketScanSubscriptionSource getMarketScanSubscriptionSource() {
		return marketScanSubscriptionSource;
	}
	/**
	 * Returns the associated marketDepthEventSubscriptionSource(IMarketDepthEventSubscriptionSource) (injected)
	 * @return
	 */
	public IMarketDepthEventSubscriptionSource getMarketDepthEventSubscriptionSource() {
		return marketDepthEventSubscriptionSource;
	}
	/**
	 * Returns the associated tickerNewsSubscriptionSource(ITickerNewsSubscriptionSource) (injected)
	 * @return
	 */
	public ITickerNewsSubscriptionSource getTickerNewsSubscriptionSource() {
		return tickerNewsSubscriptionSource;
	}
	/**
	 * Returns the associated scanCriteriaDao(IScanCriteriaDao) (injected)
	 * @return
	 */
	public IScanCriteriaDao getScanCriteriaDao() {
		return scanCriteriaDao;
	}
	/**
	 * Returns the associated contractDetailsSource(IBContractDetailsSource) (injected)
	 * @return
	 */
	public IBContractDetailsSource getContractDetailsSource() {
		return contractDetailsSource;
	}
	/**
	 * Returns the associated specificationDetailsDao(ISpecificationDetailsDao) (injected)
	 * @return
	 */
	public ISpecificationDetailsDao getSpecificationDetailsDao() {
		return specificationDetailsDao;
	}
	/**
	 * Returns the associated instrumentSpecificationDao(ISpecificationDao) (injected)
	 * @return
	 */
	public ISpecificationDao getInstrumentSpecificationDao() {
		return instrumentSpecificationDao;
	}
	/**
	 * Returns the associated candleDao(ICandleDao) (injected)
	 * @return
	 */
	public ICandleDao getCandleDao() {
		return candleDao;
	}
	/**
	 * Returns the associated quoteDao(IQuoteDao) (injected)
	 * @return
	 */
	public IQuoteDao getQuoteDao() {
		return quoteDao;
	}
	/**
	 * Returns the associated tickerNewsDao(ITickerNewsDao) (injected)
	 * @return
	 */
	public ITickerNewsDao getTickerNewsDao() {
		return tickerNewsDao;
	}
	/**
	 * Returns the associated tradeIndicationDao(ITradeIndicationDao) (injected)
	 * @return
	 */
	public ITradeIndicationDao getTradeIndicationDao() {
		return tradeIndicationDao;
	}
	/**
	 * Returns the associated marketDepthDao(IMarketDepthDao) (injected)
	 * @return
	 */
//	public IMarketDepthDao getMarketDepthDao() {
//		return marketDepthDao;
//	}
	/**
	 * Returns the associated marketDepthEventDao(IMarketDepthEventDao) (injected)
	 * @return
	 */
	public IMarketDepthEventDao getMarketDepthEventDao() {
		return marketDepthEventDao;
	}
	/**
	 * Returns the associated marketScanDao(IMarketScanDao) (injected)
	 * @return
	 */
	public IMarketScanDao getMarketScanDao() {
		return marketScanDao;
	}
	/**
	 * Returns the associated brokerAccountDao(IBrokerAccountDao) (injected)
	 * @return
	 */
	public IBrokerAccountDao getBrokerAccountDao() {
		return brokerAccountDao;
	}
	/**
	 * Returns the associated marketScanChangeListener(IEventListener&lt;MarketScanChange&gt;)
	 * @return
	 */
	public IEventListener<MarketScanChange> getMarketScanChangeListener(){
		return marketScanChangeListener;
	}
	/**
	 * Returns the associated marketScanListener(IEventListener&lt;MarketScan&gt;)
	 * @return
	 */
	public IEventListener<MarketScan> getMarketScanListener(){
		return marketScanListener;
	}
	
	public MarketScanHelper getMarketScanHelper(){
		return marketScanHelper;
	}
	/**
	 * Start<br/> 
	 * <strong>1.</strong> Sets/initializes the associated mainPlot(IPlotService&lt;Double&gt;), signalPlot(IPlotService&lt;Double&gt;), execPlot(IPlotService&lt;Double&gt;),
	 * priceSink(IPlotService.ISeriesService&lt;Double&gt;), fastPriceSink(IPlotService.ISeriesService&lt;Double&gt;), slowPriceSink(IPlotService.ISeriesService&lt;Double&gt;),
	 * slowPriceSinkMin(IPlotService.ISeriesService&lt;Double&gt;), slowPriceSinkMax(IPlotService.ISeriesService&lt;Double&gt;), fastGrowthSink(IPlotService.ISeriesService&lt;Double&gt;)
	 * and execSink(IPlotService.ISeriesService&lt;Double&gt;).<br/>
	 * <strong>2.</strong> Gets/creates and activates an s(ISubscription&lt;TradeIndication&gt;) for the associated spec(InstrumentSpecification) from the associated
	 * tradeIndicationSource(ITradeIndicationSubscriptionSource). It also attaches a listener to invoke the <code>onTradeIndication(price, quantity, event.getTimeStamp())</code>
	 * method for each received event(TradeIndication).
	 * @throws Exception
	 */
	@Start
	public void start() throws Exception {		
		log.info("starting");
		//BrokerAccount brokerAccount=brokerAccountDao.find(1);
		
//		if(broker instanceof IBAccountManagingBroker){
//			IBAccountManagingBroker IBAccountManagingBroker=(IBAccountManagingBroker)broker;
//			brokerAccount=IBAccountManagingBroker.getBrokerAccount();
//			Iterator<Position> iterator = brokerAccount.getPortfolio().getPositionIterator();
//			while(iterator.hasNext()){
//				Position position=iterator.next();
//				instrumentSpecificationDao.update(position.getInstrumentSpecification());
//			}
//			brokerAccountDao.update(brokerAccount);
//		}

		marketScanChangeListener=new MarketScanChangeListenerHandlingStrategy(this);// this also looks through portfolio positions		
		marketScanListener=new MarketScanListenerHandlingStrategy(this);
		if(broker instanceof IBPortfolioManagingBroker){
			IBPortfolioManagingBroker IBPortfolioManagingBroker =(IBPortfolioManagingBroker)broker;
			IBPortfolioManagingBroker.setMarketScanChangeListenerHandlingStrategy((MarketScanChangeListenerHandlingStrategy)marketScanChangeListener);//this gives the brokerManager access to update portfolio positions
		}
		if(RunMe.mode==Mode.LIVE){
			try {
				log.info("sleeping 4 sec to let initial updatePortfolio and other stuff in...");
				Thread.sleep(4000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		getIbConnection().setPreviouslyOpenedOrdersAlreadyLoaded(true);// at this point any existing openOrder calls from IB have been made
//		System.out.println("here");
		marketScanHelper=new MarketScanHelper(TraderProxy.this);
		boolean analyzeFOREXPairs = false;//this will register 26 Currency Pairs and no scanners
	  if(RunMe.mode==Mode.LIVE){
		  boolean lookforOpportunities=true;
		  boolean useMarketScanners=false;//if this is false we need to specify hardcoded instruments
		  if(analyzeFOREXPairs){
			  List<InstrumentSpecification> currencyPairs=CurrencyPairs.getPairs();
			  for(InstrumentSpecification currencyPair:currencyPairs){				  
				  currencyPair=getInstrumentSpecificationDao().update(currencyPair);
				  ((MarketScanChangeListenerHandlingStrategy)marketScanChangeListener).analyzeInstrument(currencyPair);
			  }
			  return;
		  }
		  
		  
		  if(useMarketScanners){
			if(lookforOpportunities){
//				opportunityWatch=new OpportunityWatch(this);
	//			boolean test=true;
	//			if(test){
				marketScanHelper.getFixedScanners();
	//			}else{
	//				opportunityWatch.scan();
	//				opportunityWatch.getOpportunities();
	//			}
			}
			boolean lookForMarketScanChanges=false;
			if(lookForMarketScanChanges){
				for(ScanCriteria criteria:marketScanHelper.getScanCriterias()){
					IMarketScanSubscription<MarketScanChange> subscription = marketScanChangeSubscriptionSource.subscribe(criteria);
					
					subscription.addEventListener(marketScanChangeListener);	
					subscription.activate();
				
				}
			}
		  }else{		  	  
			  InstrumentSpecification spec1=new InstrumentSpecification(new Symbol("IBM"), "SMART", "IB",
						Currency.USD, SecurityType.STOCK);
			  ((MarketScanChangeListenerHandlingStrategy)marketScanChangeListener).analyzeInstrument(spec1);
			  
		  }
	  }else{//backtest
		  if(!analyzeFOREXPairs){//forex has no scanners
			  //subscribe to scanners without waiting for data to come through to confirm its working		
			  new Thread(new Runnable() {
				public void run() {
					try {
						Thread.sleep(4000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					marketScanHelper.getFixedScanners();				
				}
			}).start();
		  }		  
		  List<InstrumentSpecification> specs=new ArrayList<InstrumentSpecification>();
		  
		  List<String> currenciesToWatch =null;//Arrays.asList(new String[]{"USD","CHF"});//only register for pairs having this currency for easier debugging, leave empty to register for everything
		  if(analyzeFOREXPairs){
		  List<InstrumentSpecification> currencyPairs=CurrencyPairs.getPairs();
		  	for(InstrumentSpecification currencyPairSpec:currencyPairs){
		  		if(currenciesToWatch!=null && !currenciesToWatch.isEmpty() && !(currenciesToWatch.contains(currencyPairSpec.getSymbol().toString())||currenciesToWatch.contains(currencyPairSpec.getCurrency().toString()))){
		  			continue;
		  		}
		  		currencyPairSpec=getInstrumentSpecificationDao().update(currencyPairSpec);
		  		((MarketScanChangeListenerHandlingStrategy)marketScanChangeListener).analyzeInstrument(currencyPairSpec);
		  		specs.add(currencyPairSpec);
		    }
		  	BacktestRunner.NUMBER_OF_FOREX_PAIRS=specs.size();
		  }else{
		  

			  
			  InstrumentSpecification spec=new InstrumentSpecification(new Symbol("IBM"), "SMART", "IB",
						Currency.USD, SecurityType.STOCK);
			  ((MarketScanChangeListenerHandlingStrategy)marketScanChangeListener).analyzeInstrument(spec);
			  
			  specs.add(spec);

			  
		  }
		  BacktestRunner.NUMBER_OF_INSTRUMENTS=specs.size()-BacktestRunner.NUMBER_OF_FOREX_PAIRS;
		  ScanBacktestRunner.NUMBER_OF_SCANNERS=1;
	  }				
	}
	
	/**
	 * An empty method
	 * @throws Exception
	 */
	public void stop() throws Exception {
	}

}
